package signature

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/wzh200x/webServer/constants"
	"github.com/wzh200x/webServer/middleware/response"
)

// SignatureConfig 签名验证配置
type SignatureConfig struct {
	Enable     bool     // 是否启用签名验证
	SecretKey  string   // 签名密钥
	ExpireTime int64    // 签名有效期（秒）
	Whitelist  []string // 白名单，其中的URL不需要校验签名
}

// 默认配置
var defaultConfig = SignatureConfig{
	Enable:     true,
	SecretKey:  "default_secret_key", // 默认密钥，应该通过配置文件设置
	ExpireTime: 10,                   // 默认10秒过期
}

// 全局配置实例
var signatureConfig = defaultConfig

// SetSignatureConfig 设置签名验证配置
func SetSignatureConfig(config SignatureConfig) {
	signatureConfig = config
}

// GetSignatureConfig 获取当前签名验证配置
func GetSignatureConfig() SignatureConfig {
	return signatureConfig
}

// EnableSignature 启用签名验证
func EnableSignature() {
	signatureConfig.Enable = true
}

// DisableSignature 禁用签名验证
func DisableSignature() {
	signatureConfig.Enable = false
}

// SignatureMiddleware 签名验证中间件
func SignatureMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 如果未启用签名验证，直接通过
		if !signatureConfig.Enable {
			c.Next()
			return
		}

		// 检查URL是否在白名单中
		for _, path := range signatureConfig.Whitelist {
			if path == c.Request.URL.Path {
				c.Next()
				return
			}
		}

		// 获取响应函数
		response.GetResponseFuncs(c)

		// 获取请求参数
		var params map[string][]string
		method := c.Request.Method

		// 根据请求方法获取参数
		if method == "GET" {
			params = c.Request.URL.Query()
		} else if method == "POST" {
			// 对于POST请求，尝试解析表单数据
			if err := c.Request.ParseForm(); err != nil {
				response.Error(c, constants.ErrSignatureInvalid, "无法解析请求参数")
				c.Abort()
				return
			}
			params = c.Request.Form
		} else {
			// 对于其他请求方法，尝试解析表单数据
			if err := c.Request.ParseForm(); err != nil {
				response.Error(c, constants.ErrSignatureInvalid, "无法解析请求参数")
				c.Abort()
				return
			}
			params = c.Request.Form
		}

		// 检查必要参数
		timeStampArr, hasTime := params["time"]
		signArr, hasSign := params["sign"]

		if !hasTime || !hasSign || len(timeStampArr) == 0 || len(signArr) == 0 {
			response.Error(c, constants.ErrSignatureMissParams, "缺少必要参数time或sign")
			c.Abort()
			return
		}

		timeStamp := timeStampArr[0]
		sign := signArr[0]

		// 验证时间戳
		timeInt := gconv.Int64(timeStamp)
		if timeInt == 0 {
			response.Error(c, constants.ErrSignatureInvalid, "时间戳格式错误")
			c.Abort()
			return
		}

		// 检查时间戳是否过期
		currentTime := time.Now().Unix()
		if currentTime-timeInt > signatureConfig.ExpireTime {
			response.Error(c, constants.ErrSignatureExpired, "请求已过期")
			c.Abort()
			return
		}

		// 验证签名
		calculatedSign := calculateSignature(params, signatureConfig.SecretKey)
		if sign != calculatedSign {
			response.Error(c, constants.ErrSignatureInvalid, "签名验证失败")
			c.Abort()
			return
		}

		// 验证通过，继续处理请求
		c.Next()
	}
}

// calculateSignature 计算签名
// 签名规则：按照key=value&key=value的格式拼接所有参数（按key字母顺序排序），
// 然后加上签名密钥，最后计算MD5值
func calculateSignature(params map[string][]string, secretKey string) string {
	// 创建一个新的参数映射，排除sign参数
	paramsWithoutSign := make(url.Values)
	for key, values := range params {
		if key != "sign" {
			paramsWithoutSign[key] = values
		}
	}

	// 获取所有参数的key并排序
	keys := make([]string, 0, len(paramsWithoutSign))
	for key := range paramsWithoutSign {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	// 按照key=value&key=value的格式拼接参数
	var builder strings.Builder
	for i, key := range keys {
		if i > 0 {
			builder.WriteString("&")
		}
		builder.WriteString(key)
		builder.WriteString("=")
		builder.WriteString(paramsWithoutSign.Get(key))
	}

	// 加上签名密钥
	builder.WriteString(secretKey)

	// 计算MD5
	data := builder.String()
	hash := md5.Sum([]byte(data))
	return hex.EncodeToString(hash[:])
}

// GenerateSignature 生成签名（用于测试和调试）
func GenerateSignature(params map[string]string, secretKey string) (string, string) {
	// 当前时间戳
	timeStamp := fmt.Sprintf("%d", time.Now().Unix())

	// 添加时间戳到参数中
	paramsCopy := make(map[string]string)
	for k, v := range params {
		paramsCopy[k] = v
	}
	paramsCopy["time"] = timeStamp

	// 转换为url.Values格式
	urlValues := make(url.Values)
	for k, v := range paramsCopy {
		urlValues.Add(k, v)
	}

	// 计算签名
	sign := calculateSignature(urlValues, secretKey)

	// 返回时间戳和签名
	return timeStamp, sign
}
